"""
author：@cemeye
finish_time：2023/5
version:0.0.1
mail:你猜

你好！这里是cemeye！没错，又是我，这次来看看这个模块的作用！

这个模块，完全没有用到任何第三方库，只通过Python内置函数和方法
创建了一种名为VSP的标记语言！这是一种类似于kv语言的标记语言（如果你用过kivy的话）
没用过也没关系

这个语言的特色：
    1.更加清晰的逻辑结构
    2.方便地进行增删查改和生成
    3.可以导入已经存在的VSP文件，也可以通过程序动态生成再保存在文件中
    4.支持内嵌python代码，不过不是直接在文件中写python代码哦

这个模块最初是我为了写一个Python-Android模块Virgo时想用来动态翻译为XML的，不过我觉得这个模块
很好用，于是就把它单独分离和重构了！

祝你喜欢！

"""
__author__ = "cemeye"
__finish_time__ = "2023/5"
__version__ = "0.0.1"


class VSPRunCodeError(Exception):
    """
    在运行VSP中的代码报错时
    """


class VSPUnknownCommandError(Exception):
    """
    VSP命令未发现时报错
    """
    ...


class VSPModuleNotFoundError(Exception):
    """
    VSP 在导入模块时错误
    """
    ...


class VSPIncludeMouldError(Exception):
    """
    VSP 在包含模块时可能会报错
    """
    ...


class VSP_PT_NotFoundError(Exception):
    """
    VSP @PretreatmentEnd 终止符未发现时会报错
    """
    ...


class VSP_TabAttributeNotDefineError(Exception):
    """
    属性未定义时报错
    """
    ...


class VSP_SonNotFoundError(Exception):
    """
    未发现直系子节点时报的错
    """


class VSP_DeepSonNotFoundError(Exception):
    """
    深度遍历删除子节点时未发现报的错误
    """


# 错误定义完毕，下面是对应代码

class VSPGenerateControl:
    """
    生成器,根据获取的Tab进行生产文字
    """

    def __init__(self, VSP_tab=None):
        self.final_result = []
        if not VSP_tab:
            # *VSPROOT[@vsp_root]:
            self.tab = VSPTab().set_name("VSPROOT").set_ID("@vsp_root")
        else:
            self.tab = VSP_tab

    def add_son(self, tag_name, tag_id):
        """
        添加一个直系后代
        :param tag_id:
        :param tag_name:
        :return:
        """
        self.tab.set_son(VSPTab().set_ID(tag_id).set_name(tag_name))

    def deep_add_son(self, target_tag_ID, tag_name, tag_id):
        """
        深度遍历，添加子孙节点
        :param target_tag_ID: 目标父节点
        :param tag_id: 标签ID
        :param tag_name: 标签名
        :return: None
        """
        self.get_target_by_ID(target_tag_ID).set_son(VSPTab().set_name(tag_name).set_ID(tag_id))

    def add_attribute(self, ID, attr_name, value):
        """
        浅层遍历，为直系节点设置属性
        :param ID: 目标ID
        :param attr_name:属性名
        :param value: 属性值
        :return: None
        """
        for i in self.tab.son:
            if i.ID == ID:
                i.set_attribute(attribute_name=attr_name, value=value)
                return
        raise VSP_SonNotFoundError("未找到目标节点ID 为\"{ID}\"的目标！")

    def deep_add_some_attributes(self, ID, attr_value_dict):
        """
        深度批量添加属性
        :param ID: None
        :param attr_value_dict:属性-值 字典
        :return: None
        """
        try:
            target = self.get_target_by_ID(ID)
            for i in attr_value_dict.keys():
                target.set_attribute(attribute_name=i, value=attr_value_dict[i])
        except AttributeError:
            raise VSP_SonNotFoundError("未找到目标节点ID 为\"{ID}\"的目标！")

    def deep_add_attribute(self, ID, attr_name, value):
        """
        深度遍历，查找所有子节点，为目标添加属性
        :param ID: ID
        :param attr_name:属性名
        :param value: 属性值
        :return: None
        """
        try:
            self.get_target_by_ID(ID).set_attribute(attribute_name=attr_name, value=value)
        except AttributeError:
            raise VSP_SonNotFoundError("未找到目标节点ID 为\"{ID}\"的目标！")

    def get_target_by_ID(self, ID):
        """
        通过ID获取一个VSP标签
        """
        return self.tab.get_target_by_ID(ID)

    def _recursive_struct(self, list_, index_=0):
        """
        递归生成结构体
        :param list_: None
        :param index_:
        :return:
        """
        for i in list_:
            if type(i) is str:
                self.final_result.append(index_ * "=" + "|" + i)
            else:
                self._recursive_struct(i, index_=index_ + 1)

    def generate_vsp_file(self, target_path, list_=None):
        """
        根据路径进行生成vsp文件
        :param list_: ..
        :param target_path:目标路径
        :return: None
        """
        if not list_:
            list_ = self.tab.get_recursive_struct()
        self._recursive_struct(list_)
        with open(target_path, "w", encoding="utf-8") as f:
            f.write("@PretreatmentEnd\n")
            for i in self.final_result:
                tabs = i.split("|", 1)[0].replace("=", "  ")
                result = tabs + i.split("|", 1)[1]
                print(":::", tabs, result)
                f.write(tabs + result + "\n")


class VSPTab:
    def __init__(self):
        """
        定义的，VSP标签
        """
        self.name = "_init_"
        self.ID = "_init_"
        self.father_id = "_init_"
        self.attributes = {}
        self.son = []

    def __str__(self):
        """
        魔术方法，用于返回字符串式的结果
        :return: str
        """
        return f"*{self.name}[{self.ID}]:"

    def _duplicate_removal(self):
        """
        去重，因为VSP在编译环节会通过动态添加父节点和子节点的方式，可能会有重复，
        所以需要有序去重进行修正
        :return: None
        """
        # 有序排序的以来key
        index = self.son.index
        # 去重
        self.son = list(set(self.son))
        # 有序排序
        self.son.sort(key=index)
        # 递归对于子节点去重
        for i in self.son:
            i._duplicate_removal()

    def set_ID(self, ID):
        """
        为Tab设置标签ID
        :param ID: str
        :return: self
        """
        self.ID = ID
        return self

    def set_father(self, fatherID):
        """
        为Tag设置父节点的ID
        :param fatherID: str
        :return: self
        """
        self.father_id = fatherID
        return self

    def delete_son(self, ID):
        """
        只删除直系节点
        :return: None
        """
        for i in self.son:
            if i.ID == ID:
                self.son.remove(i)
                return
        raise VSP_SonNotFoundError(
            f"未发现直系子节点ID\"{ID}\"！如果有需要，请使用deep_delete_son方法于全部子孙节点范围内删除！")

    def deep_delete_son(self, ID):
        """
        深度遍历，删除某个节点
        :param ID:目标节点的ID
        :return: None
        """
        try:
            target = self.get_target_by_ID(self.get_target_by_ID(ID).father_id).son
            for i in target:
                if i.ID == ID:
                    self.get_target_by_ID(self.get_target_by_ID(ID).father_id).delete_son(ID)
                    return
        except AttributeError:
            raise VSP_DeepSonNotFoundError(f"深度节点遍历错误！在全部子孙范围内未发现目标节点ID\"{ID}\"")

    def set_name(self, name):
        """
        设置名称
        :param name:
        :return:
        """
        self.name = name
        return self

    def set_attributes(self, attributes_names):
        """
        设置属性列表
        :param attributes_names:list
        :return:self
        """
        self.attributes_names = attributes_names
        self.attributes = {i: None for i in self.attributes_names}
        return self

    def set_attribute(self, attribute_name, value):
        """
        设置目标属性值
        :param attribute_name:
        :param value:
        :return:
        """
        try:
            self.attributes[attribute_name] = value
        except KeyError:
            # 报错可能:标签中属性未定义
            raise VSP_TabAttributeNotDefineError(f"标签{self.name}中存在未定义的属性{attribute_name}!")

    def get_sons_IDs(self):
        """
        获取直系子节点的全部ID
        :return: list[str,str.....]
        """
        names = []
        for i in self.son:
            names.append(i.ID)
        return names

    def set_son(self, son):
        """
        为标签设置子节点
        :param son: VSPTab Class
        :return: None
        """
        self.son.append(son)

    def get_target_by_ID(self, ID):
        """
        通过ID递归寻找对应Tab
        :param ID: str
        :return: None
        """
        for i in self.son:
            if i.ID == ID:
                return i
        for i in self.son:
            return i.get_target_by_ID(ID)

    def get_sons(self):
        """
        获取全部直系子节点的描述
        :return: [str,str,str......]
        """
        sons = [str(i) for i in self.son]
        return sons

    def recursive_rendering_sons(self, index=0, with_attr=False):
        """
        递归渲染子孙节点之间的关系
        通过树状结构的制表符呈现
        :param index: 固定结构
        :param with_attr: 是否一同将属性渲染出来?
        :return: None
        """
        if not with_attr:
            # 不渲染属性
            index = index
            if self.ID == "@vsp_root":
                print("[ROOT]" + index * "━━━━|" + str(self))
            for i in self.son:
                print("┣" + index * "━━━━|" + str(i))
                i.recursive_rendering_sons(index=index + 1)
        else:
            # 渲染属性
            index = index
            if self.attributes:
                print("╠" + (index - 1) * "════|" + "属性:" + str(self.attributes))
            for i in self.son:
                print("┣" + index * "━━━━|" + str(i))

                i.recursive_rendering_sons(index=index + 1, with_attr=True)

    def get_recursive_struct(self, index=0):
        """
        递归渲染子孙节点之间的关系
        通过树状结构的制表符呈现
        :param index: 固定结构
        :return: None
        """
        all_list = []
        # 渲染属性
        index = index
        for i in self.attributes.keys():
            all_list.append("-" + i + " = " + str(self.attributes[i]))
        for i in self.son:
            all_list.append(str(i))
            all_list.append(i.get_recursive_struct(index=index + 1))
        return all_list

    def process_attr_code(self, system_vars):
        """
        解析在变量赋值时的值
        :param system_vars: 系统变量字典
        :return: None
        """
        for attr_name in self.attributes.keys():
            # 当为系统变量赋值时
            target_attr_value = self.attributes[attr_name].strip()
            if target_attr_value[0] == "$":
                for system_key in system_vars.keys():
                    if system_key in target_attr_value:
                        target_attr_value = target_attr_value.replace("$" + system_key,
                                                                      f"system_vars[\"{system_key}\"]")
                        try:
                            target_attr_value = eval(target_attr_value)
                        except Exception:
                            raise VSPRunCodeError(f"在运行属性解析时出错！出错位置:{attr_name}")
                        self.set_attribute(attribute_name=attr_name, value=target_attr_value)
            # 当为python表达式时
            elif target_attr_value.strip().split(".")[0] == "~vsp":
                try:
                    self.set_attribute(attribute_name=attr_name, value=eval(target_attr_value.strip().split(".")[1]))
                except Exception:
                    raise VSPRunCodeError(f"在运行属性解析时出错！出错位置:{attr_name}")
            else:
                try:
                    self.set_attribute(attribute_name=attr_name, value=eval(target_attr_value))
                except Exception as e:
                    raise VSPRunCodeError(f"在运行属性解析时出错！赋值不合法！出错位置:{attr_name}\n错误原因:{str(e)}")

        for i in self.son:
            i.process_attr_code(system_vars)


class FormatCodeParser:
    """
    VSP编码解析器
    """

    def __init__(self, lines, vsf):
        """
        需要传参
        :param lines:VSPFile Class的 code_lines属性
        """
        self.vsf_system_var = vsf.system_var
        self.root_tab = None
        self.lines = lines
        # 预处理，添加首行
        self._pretreatment_lines()

        self.line_dict = {}

        self._process_tabs(self.lines)

        self.generate_tabs()
        # 处理运行时的VSP代码，属性中的值
        self.process_code_in_var()

    def _pretreatment_lines(self):
        """
        预处理，添加首行
        :return:
        """
        new_list = ["*VSPROOT[@vsp_root]:"]
        for i in self.lines:
            i = "    " + i
            new_list.append(i)

        self.lines = new_list

    def _process_tabs(self, lines):
        """
        初步处理标签
        :param lines:行状文字,列表
        :return:None
        """
        new_lines = []
        # 初步替换制表符或四个空格为₪,方便之后的处理
        for i in lines:
            new_line = i.replace("    ", "₪")
            new_lines.append(new_line)
        max_str = max([i.count('₪') for i in new_lines])
        line_dict = {str(i): [] for i in range(max_str + 1)}

        # 通过₪符号,确定父子关系,进行二次赋值字符串
        his_p = {}
        parent = [1, ""]
        for index, ele in enumerate(new_lines):
            len_str = ele.count("₪")
            if len_str > parent[0]:
                parent[0] = len_str
                parent[1] = new_lines[index - 1].replace("₪", "")

                his_p[str(len_str)] = new_lines[index - 1].replace("₪", "")
            elif len_str < parent[0]:
                try:
                    parent[0] = len_str
                    parent[1] = his_p[str(len_str)]
                except KeyError:
                    ...
            line_dict[str(len_str)].append(ele.replace("₪", "") + "►►" + parent[1])

        self.line_dict = line_dict

    @staticmethod
    def _process_attr(attr_str):
        """
        处理属性节点,返回名称和对应值
        :param attr_str: str
        :return:
        """
        attrs = attr_str.strip().split("►►")[0].split("=")
        name = attrs[0].strip()
        value = attrs[1].strip()
        return name, value

    @staticmethod
    def get_father(star_tag_str):
        """
        获取父节点
        :param star_tag_str:str
        :return:None
        """
        try:
            father = star_tag_str.strip().split("►►")[1].split("[", 1)[1][0:-2]
            return father
        except IndexError:
            return "@Root"

    @staticmethod
    def process_tag(tag_str):
        """
        处理标签,返回类型,标签名,标签ID
        :param tag_str: str
        :return:None
        """
        tag_name = tag_str.strip().split("[", 1)[0][1:]
        tag_type = tag_str.strip().split("[", 1)[0][0]
        if tag_type == "*":
            tag_ID = tag_str.strip().split("►►")[0].split("[", 1)[1][0:-2]
        else:
            tag_ID = tag_str.strip().split("►►")[1].split("[", 1)[1][0:-2]
        return tag_type, tag_name, tag_ID

    def generate_tabs(self):
        """
        生成标签
        :return: None
        """
        all_tags = []
        for key in self.line_dict.keys():
            line = self.line_dict[key]
            for tag in line:
                # 当标签为*时的处理
                if tag[0] == "*":
                    tag_father_id = self.get_father(tag)
                    tag_type, tag_name, tag_ID = self.process_tag(tag)
                    tag_new = VSPTab().set_ID(tag_ID).set_name(tag_name).set_father(tag_father_id)

                    all_tags.append(tag_new)
                    for i in all_tags:
                        if i.ID == tag_father_id:
                            i.set_son(tag_new)
                            break

                    for i in all_tags:
                        for ii in all_tags:
                            if ii.ID == i.father_id:
                                ii.set_son(i)
                            elif ii.get_target_by_ID(i.father_id):
                                ii.get_target_by_ID(i.father_id).set_son(i)
                # 当标签为-时的处理
                elif tag[0] == "-":
                    tag_father_id = self.get_father(tag)
                    for i in all_tags:
                        name, value = self._process_attr(tag)
                        if i.ID == tag_father_id:
                            i.set_attribute(name, value)
                            break
                        elif i.get_target_by_ID(tag_father_id):
                            i.get_target_by_ID(tag_father_id).set_attribute(name, value)
        # 进行批量去重,并且对根节点进行赋值
        for i in all_tags:
            i._duplicate_removal()
            if i.father_id == "@Root":
                self.root_tab = i

    def get_root_tag(self):
        """
        获取根节点
        :return:self.root_Tab
        """
        return self.root_tab

    def process_code_in_var(self):
        """
        有一些属性是由VSP系统变量赋值，或者Python表达式，需要解析并且更改赋值
        :return: None
        """
        self.root_tab.process_attr_code(system_vars=self.vsf_system_var)


class VirgoSpicaFile:
    """
    VSP File 类，调用前面的FormatCodeParser，
    通过构建VSP Tab来进行解析
    """

    def __init__(self, path):
        self.path = path
        self.file_lines = []
        self.code_lines = []
        self.system_var = {}
        self.include_pyfiles_paths = {}
        for line in open(self.path, 'r', encoding="utf-8"):  # 打开文件
            rs = self._process_formal(line.rstrip('\n'))
            if rs:
                self.file_lines.append(rs)
        # 预处理划分
        self._divide_pretreatment(self.file_lines)

        # 处理特殊转义字符
        self._first_marks()
        # 处理系统变量
        self._process_system_var()
        # 处理引入库
        self._process_include_py()

        # 处理系统变量中运行的代码
        self._process_runcode_var()

        # 运行单行代码
        self._run_line_code()

    def _divide_pretreatment(self, lines):
        """
        划分代码区和预处理区
        :param lines:
        :return:
        """
        try:
            index = lines.index("@PretreatmentEnd")
            self.code_lines = self.file_lines[index + 1:]
            self.file_lines = self.file_lines[0:index]

            # print(self.code_lines, self.file_lines)
        except ValueError:
            raise VSP_PT_NotFoundError("未发现VSP终止符\"@PretreatmentEnd\",请在代码中添加！")

    def _run_line_code(self):
        """
        运行单行代码
        :return: None
        """
        for i in self.file_lines:
            if i.strip()[0] == "~":
                command = i.strip().split(".", 1)[1]
                try:
                    exec(command)
                except SyntaxError:
                    try:
                        target_lib = self.include_pyfiles_paths[command.split(".", 1)[0]]
                        command_new = command.split(".", 1)[1]
                        exec(f"""target_lib.{command_new}""")
                    except KeyError:
                        error_lib_name = command.split(".", 1)[0]
                        raise VSPModuleNotFoundError(f"并没有找到名为[{error_lib_name}]的模块！")
                except NameError:
                    target_lib = self.include_pyfiles_paths[command.split(".", 1)[0]]
                    command_new = command.split(".", 1)[1]
                    exec(f"""target_lib.{command_new}""")

    def _first_marks(self):
        """
        替换特殊符号
        反斜杠+单，双引号替换为ф
        反斜杠+n，替换为∏
        :return: None
        """
        new_list = []
        for i in self.file_lines:
            i = i.replace("\\\"", "ф")
            i = i.replace("\\\'", "ф")
            i = i.replace("\\\n", "∏")
            new_list.append(i)
        self.file_lines = new_list

    @staticmethod
    def _process_formal(line):
        """
        预处理数据，删掉注释和空行
        :return: line
        """
        if line:
            if not line.strip()[0] == "#":
                return line
        return None

    def _process_system_var(self):
        """
        处理系统变量
        :return: None
        """
        for i in self.file_lines:
            if i.strip()[0] == "$":
                result = i.replace("$", "").strip().split("=", 1)
                if "~vsp" not in result[1].strip():
                    self.system_var[result[0].strip()] = eval(result[1].strip())
                else:
                    self.system_var[result[0].strip()] = result[1].strip()

    def _process_include_py(self):
        """
        解析包含进来的py文件
        :return: None
        """
        for i in self.file_lines:
            if i.strip()[0] == "@":
                if "include" in i:
                    lib_name = i.split("=")[1].strip()[1:-1]
                    if "," not in lib_name:
                        try:
                            lib = __import__(f"{lib_name}")
                            self.include_pyfiles_paths[lib_name] = lib
                        except ValueError:
                            ...
                        except ModuleNotFoundError:
                            raise VSPIncludeMouldError(f"导入Python文件失败！错误文件：{lib_name}")
                    else:
                        libs = lib_name.split(",")
                        for a_lib in libs:
                            try:
                                lib = __import__(f"{a_lib}")
                                self.include_pyfiles_paths[a_lib] = lib
                            except ValueError:
                                ...
                            except ModuleNotFoundError:
                                raise VSPIncludeMouldError(f"导入Python文件失败！错误文件：{a_lib}")
                else:
                    raise VSPUnknownCommandError(f"未知vsp命令{i}!您正在使用'@'操作符来引入文件，但是语法似乎并不正确")

    def _process_runcode_var(self):
        """
        处理运行代码
        :return: None
        """

        for key in self.system_var.keys():
            if self.system_var[key][0:5] == "~vsp.":
                command = self.system_var[key].split(".", 1)[1]
                try:
                    self.system_var[key] = eval(command)
                except SyntaxError:
                    try:
                        target_lib = self.include_pyfiles_paths[command.split(".", 1)[0]]
                        command_new = command.split(".", 1)[1]
                        self.system_var[key] = eval(f"""target_lib.{command_new}""")
                    except KeyError:
                        error_lib_name = command.split(".", 1)[0]
                        raise VSPModuleNotFoundError(f"并没有找到名为[{error_lib_name}]的模块！")
                except NameError:
                    target_lib = self.include_pyfiles_paths[command.split(".", 1)[0]]
                    command_new = command.split(".", 1)[1]
                    self.system_var[key] = eval(f"""target_lib.{command_new}""")


class VSPLoader:
    """
    VSP现存文件导入器
    作用：如果你已经写好了一个VSP文件，你可以用这个类进行读取和更改
    PS：这里面很多函数都是对于VSPTab的再度封装
    """

    def __init__(self, path):
        self.vspf = VirgoSpicaFile(path)
        self.root_tag = FormatCodeParser(self.vspf.code_lines, self.vspf).get_root_tag()

    def delete_son(self, ID):
        """
        只删除直系节点
        :return: None
        """
        self.root_tag.delete_son(ID=ID)

    def deep_delete_son(self, ID):
        """
        深度遍历，删除某个节点
        :param ID:目标节点的ID
        :return: None
        """
        self.root_tag.deep_delete_son(ID=ID)

    def set_attribute(self, attribute_name, value):
        """
        设置目标属性值
        :param attribute_name:
        :param value:
        :return:
        """
        self.root_tag.set_attribute(attribute_name=attribute_name, value=value)

    def get_sons_IDs(self):
        """
        获取直系子节点的全部ID
        :return: list[str,str.....]
        """
        return self.root_tag.get_sons_IDs()

    def set_son(self, son):
        """
        为标签设置子节点
        :param son: VSPTab Class
        :return: None
        """
        self.root_tag.set_son(son)

    def get_target_by_ID(self, ID):
        """
        通过ID递归寻找对应Tab
        :param ID: str
        :return: None
        """
        return self.root_tag.get_target_by_ID(ID=ID)

    def get_sons(self):
        """
        获取全部直系子节点的描述
        :return: [str,str,str......]
        """
        return self.root_tag.get_sons()

    def recursive_rendering_sons(self, index=0, with_attr=False):
        """
        递归渲染子孙节点之间的关系
        通过树状结构的制表符呈现
        :param index: 固定结构
        :param with_attr: 是否一同将属性渲染出来?
        :return: None
        """
        self.root_tag.recursive_rendering_sons(index=index, with_attr=with_attr)

    def get_recursive_struct(self, index=0):
        """
        递归渲染子孙节点之间的关系
        通过树状结构的制表符呈现
        :param index: 固定结构
        :return: None
        """
        return self.root_tag.get_recursive_struct(index=index)


class VSP:
    """
    为了方便大家使用，我特意将上面不同类型的类进行了封装
    这个是真正的整合类，支持全部功能
    """

    def __init__(self, path=""):
        # 导入器
        if path:
            self.loader = VSPLoader(path=path)
        else:
            self.loader = None
            # 生成器
        self.maker = VSPGenerateControl()

    def recursive_rendering_sons(self, with_attr=True):
        """
        根节点渲染
        :param with_attr:是否渲染属性
        :return: None
        """
        if self.loader:
            if self.loader.vspf.system_var:
                print("┏系统变量列表")
                for i in self.loader.vspf.system_var.keys():
                    print(f"┗━━[{i}]━━━|{self.loader.vspf.system_var[i]}|")
            self.loader.root_tag.recursive_rendering_sons(with_attr=with_attr)
        else:
            self.maker.tab.recursive_rendering_sons(with_attr=with_attr)

    def get_tab(self):
        """
        获取根标签
        :return:
        """
        if self.loader:
            return self.loader.root_tag
        else:
            return self.maker.tab


if __name__ == "__main__":
    vsp = VSP("try.vsp")
    vsp.recursive_rendering_sons()
    print(vsp.get_tab().son)
